package actor

import (
	"sync"

	kadefine "kiwi/actor/define"
	kaevent "kiwi/actor/event"
	kanetmsgi "kiwi/actor/net/message/inside"
	kanetmsgo "kiwi/actor/net/message/outside"
	katimer "kiwi/actor/timer"
	kawork "kiwi/actor/work"
	kdebug "kiwi/debug"
	kdefine "kiwi/define"
)

type actor struct {
	kdefine.IActor

	startedWaitGroup *sync.WaitGroup

	id string

	startedOnce  sync.Once
	stoppingOnce sync.Once
	stoppedOnce  sync.Once
}

func create(iActor kdefine.IActor) *actor {
	a := &actor{
		IActor:           iActor,
		startedWaitGroup: iActor.StartedWaitGroup(),
		id:               iActor.ActorID(),
	}

	if a.startedWaitGroup == nil {
		a.startedWaitGroup = &sync.WaitGroup{}
	}

	return a
}

func (a *actor) destroy() {
	a.IActor = nil
	//a.startedWaitGroup = nil
}

func (a *actor) receive(ctx kadefine.Context) {
	switch msg := ctx.Message().(type) {
	case *kawork.MessagePostWork:
		msg.Work.Execute(ctx)
		kawork.DestroyMessagePostWork(msg)

	case *kawork.MessageDispatchWork:
		msg.Err = msg.Work.Execute(ctx)
		ctx.Respond(msg)

	case *katimer.MessageTimeout:
		defer katimer.DestroyMessageTimeout(msg)

		if a.Timer() == nil {
			return
		}

		ok, err := a.BeforeTriggerTimer(msg.ID, msg.Tag, ctx)
		if !ok {
			return
		}
		if err != nil {
			a.Logger().Errorf("[%s] execute BeforeTriggerTimerFunc fail, id=%d, tag=%d, %s", a.id, msg.ID, msg.Tag, err)
			return
		}

		err = a.Timer().Trigger(ctx, msg.ID)
		if err != nil {
			a.Logger().Errorf("[%s] trigger timer fail, id=%d, tag=%d, %s", a.id, msg.ID, msg.Tag, err)
			return
		}

		a.AfterTriggerTimer(err, msg.ID, msg.Tag, ctx)

	case kaevent.IEvent:

	case kanetmsgi.IMessage:

	case kanetmsgo.IMessage:

	case *kadefine.Started:
		a.startedOnce.Do(func() {
			defer func() {
				if p := recover(); p != nil {
					kdebug.WriteStackTraceFile(p, "")
					ctx.Stop(ctx.Self())
				}

				if a.StoppedWaitGroup() != nil {
					a.StoppedWaitGroup().Add(1)
				}

				a.startedWaitGroup.Done()
			}()

			pid := ctx.Self().Clone()
			if a.id == "" {
				a.id = pid.Id
			}

			a.OnStarted(ctx)
		})

	case *kadefine.Stopping:
		a.stoppingOnce.Do(func() {
			defer func() {
				if p := recover(); p != nil {
					kdebug.WriteStackTraceFile(p, "")
				}
			}()

			a.OnStopping(ctx)
		})

	case *kadefine.Stopped:
		a.stoppedOnce.Do(func() {
			defer func() {
				if p := recover(); p != nil {
					kdebug.WriteStackTraceFile(p, "")
				}

				if a.Timer() != nil {
					a.Timer().StopAll()
				}

				if a.StoppedWaitGroup() != nil {
					a.StoppedWaitGroup().Done()
				}

				a.destroy()
			}()

			a.OnStopped(ctx)
		})

	case *kadefine.Restarting:
		//defer func() {
		//	if p := recover(); p != nil {
		//		kdebug.WriteStackTraceFile(p, "")
		//	}
		//}()

		a.OnRestarting(ctx)

	case *kadefine.Terminated:
		a.OnTerminate(ctx, msg.Who, msg.Why)

	default:
		a.OnReceive(ctx)
	}
}
